/*******************************************************************************
 * Copyright (c) 2010 liw.
 * All rights reserved. 
 * 
 * This file is part of VanBus.
 * 
 *     VanBus is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     VanBus is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 * 
 *     You should have received a copy of the GNU General Public License
 *     along with VanBus.  If not, see <http://www.gnu.org/licenses/>.
 * Contributors:
 *     liw - initial API and implementation
 ******************************************************************************/
package org.niclab.vanbus.activity;

import java.util.Calendar;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map.Entry;

import org.niclab.vanbus.R;

import org.niclab.vanbus.activity.comp.EndStopReminderDialog;
import org.niclab.vanbus.activity.comp.MenuDialog;
import org.niclab.vanbus.activity.comp.StartStopReminderDialog;
import org.niclab.vanbus.activity.controller.BusRouteController;
import org.niclab.vanbus.activity.controller.BusStopController;
import org.niclab.vanbus.application.VanBusApplication;
import org.niclab.vanbus.gps.MyLocationManager;
import org.niclab.vanbus.model.BusRoute;
import org.niclab.vanbus.model.BusStop;
import org.niclab.vanbus.model.EndStopReminder;
import org.niclab.vanbus.model.StartStopReminder;
import org.niclab.vanbus.model.viewadpater.BusRouteArrayAdapter;
import org.niclab.vanbus.model.viewadpater.BusStopArrayAdapter;
import org.niclab.vanbus.model.viewadpater.CommonListAdapter;
import org.niclab.vanbus.query.SimpleQueryWorker;
import org.niclab.vanbus.query.command.DismissDialogCmd;
import org.niclab.vanbus.query.command.ICommand;
import org.niclab.vanbus.query.command.ICommandChain;
import org.niclab.vanbus.query.command.ShowDialogCmd;
import org.niclab.vanbus.query.command.ToggleBusyLoadingPanelCmd;
import org.niclab.vanbus.query.command.UpdateListViewCmd;
import org.niclab.vanbus.query.mem.SimpleMemQueryHandler;
import org.niclab.vanbus.query.ws.GoogleDirectionWSQueryResResolver;
import org.niclab.vanbus.utils.Converter;


import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;

public class RouteListOfStopAct extends ListActivity{
	
	private static final String LOG_TAG = "RouteListOfStopAct";
	
	private final static int DIALOG_GENERAL_BUSY =1;
	private final static int DIALOG_GENERAL_ALERT=2;
	private final static int DIALOG_START_STOP_REMINDER=3;
	private final static int DIALOG_END_STOP_REMINDER=4;
	private final static int DIALOG_ROUTE_MENU=5;
	private final static int DIALOG_STOP_MENU=6;
	
	
	private BusStop busStop;
	private BusRouteController busRouteController;
	private BusStopController busStopController;
	
	
	private String dialogBusyMsg;
	private String dialogAlertMsg;
	
	private StartStopReminderDialog startStopReminderDialog;
	private EndStopReminderDialog endStopReminderDialog;
	
	private StopMenuDialog stopMenu;
	private RouteMenuDialog routeMenu;
	private CommonListAdapter model;
	private View selectedRowView;
	private int selectedRowPosition;


	
	public enum StopMenuItem{
		Show_On_Map("Show on map"),
		Add_Favorite("Add favorite"),
		Remove_Favorite("Remove favorite");
		
		private String str;
		
		StopMenuItem(String str){
			this.str= str;
		}
		
		public String toString(){
			return this.str;
		}
	}
	
	public enum RouteMenuItem{
		Update_Time_Schedule("Update time schedule"),
		Start_Stop_Reminder("Set start stop reminder"),
		End_Stop_Reminder("Set destination stop reminder"),
		View_Route_Details("View route details"),
		Add_Favorite("Add favorite"),
		Remove_Favorite("Remove favorite");
		
		private String str;
		
		RouteMenuItem(String str){
			this.str= str;
		}
		
		public String toString(){
			return this.str;
		}
	}
	
	
	private class StopMenuDialog extends MenuDialog<StopMenuItem>{

		
		
		public StopMenuDialog(Activity ownerActivity) {
			super(ownerActivity,android.R.layout.select_dialog_item,StopMenuItem.class);
			
		}


		@Override
		public void decorateMenu(AlertDialog dialog) {
			dialog.setTitle(busStop.getStopId()+" "+ busStop.getStopName());
		}

		@Override
		public void onItemSelected(StopMenuItem selected) {
			
			switch(selected){
			case Show_On_Map:viewOnMap();break;
			case Add_Favorite:addFavoriteStop();break;
			case Remove_Favorite: cancelFavoriteStop();break;
			}
		}
		

		public void prepareMenuDialog(Dialog dialog) {
			EnumSet<StopMenuItem> set = EnumSet.allOf(StopMenuItem.class);
			
			if(busStop.isFavorite()){
				set.add(StopMenuItem.Remove_Favorite);
				set.remove(StopMenuItem.Add_Favorite);
			}else{
				set.add(StopMenuItem.Add_Favorite);
				set.remove(StopMenuItem.Remove_Favorite);
			}
			super.prepareMenuDialog(dialog, set);
			
		}
		
	}

	
	private class RouteMenuDialog extends MenuDialog<RouteMenuItem>{

		BusRoute route;
		public RouteMenuDialog(Activity ownerActivity) {
			super(ownerActivity,android.R.layout.select_dialog_item,RouteMenuItem.class);
			
		}
		
		public void setBusRoute(BusRoute route){
			this.route = route;
		}


		@Override
		public void decorateMenu(AlertDialog dialog) {

			dialog.setTitle(route.getRouteId()+" " +route.getRouteDir()+" "+route.getRouteName());
				
		}

		@Override
		public void onItemSelected(RouteMenuItem selected) {
			
			switch(selected){			
			case Update_Time_Schedule: updateRouteSchedule(route); break;
			case Start_Stop_Reminder:setStartStopReminder(route);break;
			case End_Stop_Reminder: setEndStopReminder(route);break;
			case View_Route_Details:viewRouteDetail(route);break;
			case Add_Favorite: addFavoriteRoute(route); break;
			case Remove_Favorite: cancelFavoriteRoute(route);break;
			
			}
						
		}
		
		public void prepareMenuDialog(Dialog dialog){
			EnumSet<RouteMenuItem> set = EnumSet.allOf(RouteMenuItem.class);
			if(!route.isFavorite()){
				set.remove(RouteMenuItem.Remove_Favorite);
				
			}
			else{
				set.remove(RouteMenuItem.Add_Favorite);
				
			}
			
			super.prepareMenuDialog(dialog, set);
				
			
		}
		
		
	}
	
	@Override
	public void onCreate(Bundle savedInstanceState){
		
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
		this.setContentView(R.layout.bus_route_for_stop_list);
		
		
		
		//get the currently selected bus route
		Intent intent = getIntent();
		String stopKey= intent.getStringExtra(getString(R.string.currently_selected_bus_stop));
		busStop =(BusStop) ((VanBusApplication)this.getApplication()).getAndRemoveTempData(stopKey);
				
		busRouteController = new BusRouteController();
		busStopController = new BusStopController(busStop);
		initializeTitle();
		
		
		//BusStopArrayAdapter stopAdapter = new BusStopArrayAdapter(this,R.layout.bus_stop_row,busRoute);
		model = new CommonListAdapter(this, R.layout.bus_route_for_stop_row);
		this.setListAdapter(model);
		
		startRetrieveRoutes();
		
		//menus
		routeMenu = new RouteMenuDialog(this);
		stopMenu = new StopMenuDialog(this);
		//register stop menu
		//final Activity that = this;
		ListView listView = getListView();
		listView.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view, int position,
					long id) {
				//activity.openContextMenu(view);
				setSelectedRow(view,position);
				Entry<BusRoute,List<String>> entry = (Entry<BusRoute, List<String>>) model.getItem(position);
				routeMenu.setBusRoute(entry.getKey());
				showDialog(DIALOG_ROUTE_MENU);
			}
		});
		
		
		
		dialogBusyMsg= getString(R.string.dialog_loading_busy_msg);
		
	}
	
	@Override
	protected void onPrepareDialog(final int id, final Dialog dialog){
		switch (id) {
		  case DIALOG_GENERAL_BUSY:
			  ProgressDialog pDialog = (ProgressDialog)dialog;
			  pDialog.setMessage(dialogBusyMsg);
			  break;
		  case DIALOG_GENERAL_ALERT:
			  Log.d(LOG_TAG,"prepare a general alert dialog");
			  AlertDialog aDialog = (AlertDialog)dialog;
			  aDialog.setMessage(dialogAlertMsg);
			  break;
		  case DIALOG_START_STOP_REMINDER:
			  
			startStopReminderDialog.prepareDialog(dialog);
			  break;
		  case DIALOG_END_STOP_REMINDER:
			  endStopReminderDialog.prepareDialog(dialog);
			  break;
		  case DIALOG_ROUTE_MENU:
			  ((RouteMenuDialog)routeMenu).prepareMenuDialog(dialog);
			  break;
		  case DIALOG_STOP_MENU:
			  ((StopMenuDialog)stopMenu).prepareMenuDialog(dialog);
			  break;
			  
		}

		
	}
	
	@Override
	protected Dialog onCreateDialog(int id){
		Dialog dialog;
		switch(id){
		
		case DIALOG_GENERAL_BUSY:
			ProgressDialog pDialog = new ProgressDialog(this);
			pDialog.setMessage(getString(R.string.dialog_loading_busy_msg));
			pDialog.setIndeterminate(true);
			dialog = pDialog;
			break;
			
		case DIALOG_GENERAL_ALERT:
			Log.d(LOG_TAG,"creat a general alert dialog");
			AlertDialog.Builder aDialog= new AlertDialog.Builder(this);
			aDialog.setMessage(dialogAlertMsg);
			aDialog.setPositiveButton("Yes",new DialogInterface.OnClickListener() {
				
				@Override
				public void onClick(DialogInterface dialog, int which) {
					dialog.dismiss();
					
				}				
			});
			dialog = aDialog.create();
			break;
		case DIALOG_START_STOP_REMINDER:
			
			dialog = startStopReminderDialog.createDialog();
			break;
			
		case DIALOG_END_STOP_REMINDER:
			dialog=endStopReminderDialog.createDialog();
			break;
			
		case DIALOG_ROUTE_MENU:
			dialog = routeMenu.createMenuDialog();
			break;
			
		case DIALOG_STOP_MENU:
			dialog= stopMenu.createMenuDialog();
			break;
		default: dialog=null;
		}
		
		return dialog;
		
		
		
	}
	
	
	public void cancelFavoriteStop() {
		busStopController.cancelFavoriteStop(this.getApplicationContext());
		toggleFavoriteStopIcon();
		
		
	}

	public void addFavoriteStop() {
		busStopController.addFavoriteStop(this.getApplicationContext());
		toggleFavoriteStopIcon();

		
	}

	public void viewOnMap() {
		//get the stop's gps coordinates
		busStopController.setBusStop(busStop);
		final SimpleQueryWorker worker = busStopController.queryStopGeoLoc(busStop);

		// connect the pre and post commands
		this.setBusyMessage("Retrieving stop's location, Please wait ...");
		ShowDialogCmd busyDialog = new ShowDialogCmd(this, this.DIALOG_GENERAL_BUSY);

		worker.addAsyncPreCommand(busyDialog);

		// dismiss the busy dialog
		DismissDialogCmd dimissBusyDialog = new DismissDialogCmd(this,this.DIALOG_GENERAL_BUSY);
		worker.addAsyncPostCommand(dimissBusyDialog);

		final Context context =this;
		ICommand showMapView = new ICommand() {

			@Override
			public void execute(Object input, ICommandChain chain) {
								
				Intent startMap = new Intent().setClass(context, OverlayMapAct.class);
				String key = busStop.getStopId();
				
				VanBusApplication.getInstance().saveTempData(key, busStop);
				startMap.putExtra(context.getString(R.string.currently_selected_bus_stop), key);
				context.startActivity(startMap);
				chain.execute(input);

			}

		};

		worker.addWorkerPostCommand(showMapView);

		//worker.addSyncPostCmd(showMapView);
		worker.work();
		
	}

	private void setSelectedRow(View view, int position){
		this.selectedRowView = view;
		this.selectedRowPosition = position;
		
	}
	
	private int getSelectedRowPosition(){
		return this.selectedRowPosition;
		
	}
	
	private View getSelectedRowView(){
		return this.selectedRowView;
	}
	

	private void initializeTitle() {
		getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.bus_route_for_stop_title_bar);
		TextView stopName = (TextView) findViewById(R.id.bus_route_for_stop_stop_name);
		stopName.setText(busStop.getStopName());
		
		TextView stopId = (TextView) findViewById(R.id.bus_route_for_stop_stop_id);
		stopId.setText(busStop.getStopId());

		toggleFavoriteStopIcon();
		
		//register route menu
		View titleBar = findViewById(R.id.bus_route_for_stop_title_bar);
		titleBar.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {

				showDialog(DIALOG_STOP_MENU);
				
			}
		});
		
		
	}

	

	private void toggleFavoriteStopIcon() {
		ImageView fav = (ImageView) findViewById(R.id.bus_route_for_stop_stop_fav);
		if(busStop.isFavorite())		
			fav.setImageResource(android.R.drawable.star_on);
		else
			fav.setImageResource(android.R.drawable.star_off);
		
	}

	private void startRetrieveRoutes() {
		
		SimpleQueryWorker worker = busStopController.loadRoutesByStop(busStop);
		
		//Sync command
		ICommand populateModelCmd = new ICommand(){

			@Override
			public void execute(Object input, ICommandChain chain) {
				
				BusRouteArrayAdapter adapter= new BusRouteArrayAdapter(model.getContext(),model.getResourceId(),busStop.getBusTimes());
				model.setBaseAdapter(adapter);
				
				chain.execute(input);
				
			}
			
		};
		
		
		worker.addWorkerPostCommand(populateModelCmd);
		
		//PreCommand
		ICommand displayBusyPanel = new ToggleBusyLoadingPanelCmd(this);

		worker.addAsyncPreCommand(displayBusyPanel);
		
		
		
		//PostCommands
		//populate the model
		
		
		ICommand dismissBusyPanel = new ToggleBusyLoadingPanelCmd(this);
		ICommand updateViewAsync = new UpdateListViewCmd(this);		
		
		//worker.addAsyncPostCommand(populateModelCmd);
		worker.addAsyncPostCommand(dismissBusyPanel);
		worker.addAsyncPostCommand(updateViewAsync);
		
		
		//ProgressCommand
		worker.addAsyncProgressCommand(updateViewAsync);
		
		worker.work();
		
		
		
	}


	
	public void cancelFavoriteRoute(BusRoute route) {
		
		busRouteController.setBusRoute(route);
		busRouteController.cancelFavoriteRoute(this.getApplicationContext());
		toggleFavoriteRouteIcon(route);
	
		
		
	}

	private void toggleFavoriteRouteIcon(BusRoute route) {
		View rowView = getSelectedRowView();
		ImageView image = (ImageView) rowView.findViewById(R.id.bus_route_favorite_for_stop);
		if(route.isFavorite())
			image.setImageResource(android.R.drawable.star_big_on);
		else
			image.setImageResource(android.R.drawable.star_big_off);
				
		
	}

	public void addFavoriteRoute(BusRoute route) {
		
		busRouteController.setBusRoute(route);
		busRouteController.addFavoriteRoute(this.getApplicationContext());
		toggleFavoriteRouteIcon(route);

		
	}


	public void updateRouteSchedule(final BusRoute route) {
		
		SimpleQueryWorker worker = busStopController.updateStopSchedule(route);
		
		ICommand updateRowCmd =new ICommand(){

			@Override
			public void execute(Object input, ICommandChain chain) {
				route.setTimeScheduleRefreshed(true);
				getListAdapter().getView(getSelectedRowPosition(), getSelectedRowView(), getListView());
				route.setTimeScheduleRefreshed(false);
				
				
			}
			
		};
		
		worker.addAsyncPostCommand(updateRowCmd);
		worker.work();
		
	}
	public void viewRouteDetail(BusRoute route) {
		
		//register the currently selected bus route to have the be able to retrieve
		
		VanBusApplication app = (VanBusApplication)getApplication();
		String key=route.getRouteId()+route.getRouteDir();
		app.saveTempData(key, route);
		
		//start the stops list activity
		Intent startStopListIntent = new Intent();
		startStopListIntent.setClass(this,StopListOfRouteAct.class);
		startStopListIntent.putExtra(getString(R.string.currently_selected_bus_route), key);
		
		startStopListIntent.putExtra(getString(R.string.currently_selected_bus_stop), busStop.getStopId());
		
		this.startActivity(startStopListIntent);
		
	}

	private void setBusyMessage(String msg) {
		this.dialogBusyMsg = msg;

	}

	private void setAlertMessage(String msg) {
		this.dialogAlertMsg = msg;
	}
	
	
	public void setStartStopReminder(final BusRoute route) {
		
			// show reminder dialog
		if (startStopReminderDialog == null)
			startStopReminderDialog = new StartStopReminderDialog(this);

		StartStopReminder reminder = new StartStopReminder(route, busStop);
		startStopReminderDialog.setReminder(reminder);

		String title = route.getRouteId() + " "	+ route.getRouteDir() + " " + busStop.getStopName();
		Log.d(LOG_TAG, "dialog title" + title);
		startStopReminderDialog.setTitle(title);
		startStopReminderDialog.setFutureTimes(busStop.getTimesString(route));
		startStopReminderDialog.setDate(Calendar.getInstance());
		
		// get current Location
		
		MyLocationManager locMan = VanBusApplication.getInstance().getMyLocationManager();
		Log.d(LOG_TAG,"current location: "+ locMan.getCurrentBestLocation(0));
		if (!locMan.isGPSEnabled() || locMan.getCurrentBestLocation(0)==null) {
			Log.d(LOG_TAG, "No GPS available");
			startStopReminderDialog.setWalkingDistanceMessage("N/A(No GPS available)");
			startStopReminderDialog.setWalkingTimeMessage("N/A(No GPS available)");
			showDialog(DIALOG_START_STOP_REMINDER);
			return;

		}

		final SimpleQueryWorker worker = busStopController.queryWalkingDirectionToStop(null);
			
		// connect the pre and post commands
		this.setBusyMessage("Calculating walking distance and estimated time, Please wait ...");
		ShowDialogCmd busyDialog = new ShowDialogCmd(this,this.DIALOG_GENERAL_BUSY);

		worker.addAsyncPreCommand(busyDialog);

		// dismiss the busy dialog
		DismissDialogCmd dimissBusyDialog = new DismissDialogCmd(this,this.DIALOG_GENERAL_BUSY);
		worker.addAsyncPostCommand(dimissBusyDialog);

		
		
		ICommand showReminderDialog = new ICommand() {

			@Override
			public void execute(Object input, ICommandChain chain) {
				
				GoogleDirectionWSQueryResResolver.QueryResult qResult = (org.niclab.vanbus.query.ws.GoogleDirectionWSQueryResResolver.QueryResult) worker
						.getLastFinishedQuery().getQueryResult();

				String distance = Converter.convertDistancetoString(qResult.getDistance());
				String duration = Converter.convertDurationtoString(qResult.getDuration());
			
				startStopReminderDialog.setWalkingDistanceMessage(distance);
				startStopReminderDialog.setWalkingTimeMessage(duration);
				showDialog(DIALOG_START_STOP_REMINDER);

				chain.execute(input);

			}

		};

		worker.addAsyncPostCommand(showReminderDialog);
		
				
		worker.work();
		
		
	}

	public void setEndStopReminder(final BusRoute route) {
				
		busRouteController.setBusRoute(route);
		
		
		SimpleQueryWorker loadStopsWorker= busRouteController.loadBusStops();
		
		
		ICommand queryStopCoords = new ICommand(){

			@Override
			public void execute(Object input, ICommandChain chain) {
				List<BusStop> stops = route.getStops();
				
				
				BusStop prevStop;
				BusStop nextStop = null;
				
				SimpleQueryWorker worker = busStopController.queryStopGeoLoc(busStop);
				int pos =-1;
				for(int i=0; i<stops.size();i++){
					BusStop cur = stops.get(i);
					if(cur.getStopId().equalsIgnoreCase(busStop.getStopId())){
						pos = i;
						break;
					}
				}
			
				if(pos-1 <0 ){
					Log.v(LOG_TAG,"no previous stop found");
					setAlertMessage("Sorry, can not set an end stop reminder for a start stop!");
					showDialog(DIALOG_GENERAL_ALERT);
					return;
					
				}
				else{
					prevStop = stops.get(pos-1);
					worker.connectWorker(busStopController.queryStopGeoLoc(prevStop));
				}
				
				
				if(pos+1 < stops.size()){
					nextStop = stops.get(pos+1);
					worker.connectWorker(busStopController.queryStopGeoLoc(nextStop));
				}
					
				// connect the pre and post commands
				setBusyMessage("Retrieving destination stop's data. Please wait...");
				ShowDialogCmd busyDialog = new ShowDialogCmd(RouteListOfStopAct.this, DIALOG_GENERAL_BUSY);

				worker.addAsyncPreCommand(busyDialog);

				// post command: dismiss the busy dialog
				DismissDialogCmd dimissBusyDialog = new DismissDialogCmd(RouteListOfStopAct.this,DIALOG_GENERAL_BUSY);
				worker.addAsyncPostCommand(dimissBusyDialog);
							
				if (endStopReminderDialog == null) {
					endStopReminderDialog = new EndStopReminderDialog(
							RouteListOfStopAct.this);
				}
				EndStopReminder reminder = new EndStopReminder(route, prevStop,	busStop, nextStop);
				endStopReminderDialog.setReminder(reminder);
				
				// post command: show the reminder dialog
				ICommand showReminderDialog = new ICommand() {

					@Override
					public void execute(Object input, ICommandChain chain) {

						endStopReminderDialog.setTitle(route.getRouteId() + " "
								+ route.getRouteDir() + " " + busStop.getStopName());
						
					

						showDialog(DIALOG_END_STOP_REMINDER);

					}

				};

				worker.addAsyncPostCommand(showReminderDialog);
				

				worker.work();
				
				
				
			}
			
		};

		

		// connect the pre and post commands
		this.setBusyMessage("Retrieving bus route information. Please wait...");
		ShowDialogCmd busyDialog = new ShowDialogCmd(this, this.DIALOG_GENERAL_BUSY);

		loadStopsWorker.addAsyncPreCommand(busyDialog);

		// post command: dismiss the busy dialog
		DismissDialogCmd dimissBusyDialog = new DismissDialogCmd(this,
				this.DIALOG_GENERAL_BUSY);
		loadStopsWorker.addAsyncPostCommand(dimissBusyDialog);
		
		loadStopsWorker.addAsyncPostCommand(queryStopCoords);
		
		loadStopsWorker.work();

	

	}




		
		
	
	
	
	
	
	

}
